/**
 * 版本控制
 */
package indi.zjh.dp.vc;

import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.dom4j.DocumentException;

import indi.zjh.dp.error.UnsuportedFieldException;
import indi.zjh.dp.error.UnsuportedFieldTypeChangeException;
import indi.zjh.dp.orm.RelationClass;
import indi.zjh.dp.startup.Constant;
import indi.zjh.dp.util.CodecUtil;
import indi.zjh.dp.util.FileUtil;
import indi.zjh.dp.util.XMLUtil;

public class VersionControl {
	private static VersionControl instance = null;
	private Version runningTimeVersion = null;
	// 业务类sha1 -> FileObject(映射表)
	private Map<String, FileObject> cmtFileObject;
	// 提交sha1 -> 提交描述类(映射表)
	private Map<String, Commit> cmtCommitObject;
	// 提交sha1列表
	private LinkedList<String> cmtCommitObjectList;
	// 业务类代码库存放路径
	private String repositoryPath;
	
	// 业务代码类文件迭代器
	private class FileIter implements Iterator<FileObject>
	{
		Iterator<Map.Entry<String, FileObject>> iter;
		public FileIter()
		{
			iter = VersionControl.this.cmtFileObject.entrySet().iterator();
		}
		public boolean hasNext()
		{
			return iter.hasNext();
		}

		@Override
		public FileObject next() 
		{
			// TODO Auto-generated method stub
			return iter.next().getValue();
		}
	}
	
	/**
	 * 获取业务对象的迭代器
	 * @return
	 */
	public FileIter iterator()
	{
		return new FileIter();
	}
	
	private VersionControl() throws DocumentException, Exception
	{
		this.repositoryPath = XMLUtil.getString(Constant.CONF_FILE, "/Business/business-repository-path");
		this.cmtFileObject = new HashMap<String, FileObject>();
		this.cmtCommitObject = new HashMap<String, Commit>();
		this.cmtCommitObjectList = new LinkedList<String>();
	}
	
	public static VersionControl getInstance() throws DocumentException, Exception
	{
		if(instance == null)
		{
			instance = new VersionControl();
			instance.readCommitObject();
			instance.readFileObject();
			instance.runningTimeVersion = new Version();
		}
		return instance;
	}
	
	/**
	 * 把指定的业务类添加到业务代码库
	 * @param rc: 业务类
	 * @return true:业务类不存在, 添加成功, false: 业务类已经存在, 添加不成功
	 */
	public boolean addToRepository(RelationClass rc)
	{
		String clsSha = CodecUtil.fileSha1(rc.getSrcFilePath());
		File dir = new File(this.computeFileDirectory(clsSha));
		if(dir.isFile())
		{
			dir.delete();
		}
		else
		{
			if(!dir.isDirectory())
				dir.mkdirs();
		}
		String repFilePath = this.computeFilePath(clsSha);
		
		File file = new File(repFilePath);
		if(file.exists())
			return false;
		
		File fromFile = new File(rc.getSrcFilePath());
		
		try {
			FileUtil.copy(fromFile, file);
			this.cmtFileObject.put(clsSha, new FileObject(rc.getSrcFilePath(), rc.getName(), repFilePath));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return true;
	}
	
	/**
	 * 计算业务类存放在业务容器中的相对路径
	 * @param clazzSha1Text
	 * @return
	 */
	private String computeFilePath(String clazzSha1Text)
	{
		String pathPrefix = clazzSha1Text.substring(0, 2);
		return this.repositoryPath + File.separator + "class" + File.separator + pathPrefix + File.separator + clazzSha1Text;
	}
	/**
	 * 计算业务类存放在业务容器中的相对路径目录
	 * @param clazzSha1Text
	 * @return
	 */
	private String computeFileDirectory(String clazzSha1Text)
	{
		String pathPrefix = clazzSha1Text.substring(0, 2);
		return this.repositoryPath + File.separator + "class" + File.separator + pathPrefix;
	}
	
	@SuppressWarnings("unchecked")
	/**
	 * 读取所有提交的代码库的业务类hashtable
	 */
	public void readFileObject()
	{
		this.cmtFileObject = (HashMap<String, FileObject>)FileUtil.readFileObject(this.repositoryPath + File.separator + "fo");
		if(this.cmtFileObject == null)
		{
			this.cmtFileObject = new HashMap<String, FileObject>();
		}
//		Set<String> keys = this.cmtFileObject.keySet();
//		Iterator<String> iterator = keys.iterator();
//		while(iterator.hasNext())
//		{
//			FileObject fObject = this.cmtFileObject.get(iterator.next());
//		}
		
	}
	
	/**
	 * 写入提交的代码库的业务类hashtable
	 */
	public void writeFileObject()
	{
		FileUtil.writeFileObject(this.cmtFileObject, this.repositoryPath + File.separator + "fo");
	}
	
	/**
	 * 添加提交对象
	 * @param cmt
	 */
	public void addCommit(Commit cmt)
	{		
		this.cmtCommitObjectList.add(cmt.getSha1Text());
		this.cmtCommitObject.put(cmt.getSha1Text(), cmt);
	}
	
	/**
	 * 把提交对象列表序列化到文件
	 */
	public void writeCommitObject()
	{
		FileUtil.writeFileObject(this.cmtCommitObject, this.repositoryPath + File.separator + "ct");
	}
	
	/**
	 * 从文件读取提交对象列表
	 */
	@SuppressWarnings("unchecked")
	public void readCommitObject()
	{
		this.cmtCommitObject = (Map<String, Commit>)FileUtil.readFileObject(this.repositoryPath + File.separator + "ct");
		if(this.cmtCommitObject == null)
		{
			this.cmtCommitObject = new HashMap<String, Commit>();
		}

		Iterator<Map.Entry<String, Commit>> iter = this.cmtCommitObject.entrySet().iterator();
		while(iter.hasNext())
		{
			Map.Entry<String, Commit> entry = iter.next();
			String key = entry.getKey();
			Commit commit = entry.getValue();
			this.cmtCommitObjectList.add(key);
		}
		
		Collections.sort(this.cmtCommitObjectList, new Comparator(){

			@Override
			public int compare(Object o1, Object o2) {
				// TODO Auto-generated method stub
				if(o1 instanceof String && o2 instanceof String)
				{
					Commit c1 = VersionControl.this.cmtCommitObject.get(o1);
					Commit c2 = VersionControl.this.cmtCommitObject.get(o2);
					return Long.compare(c1.timestamp, c2.timestamp);
				}
				else
				{
					throw new ClassCastException("不是Commit对象");
				}
				//return 0;
			}
			
		});
	}
	
	/**
	 * 获取当前提交的版本列表, 按时间倒叙
	 * @param num: 指定获取的版本个数
	 * @return: 提交对象列表
	 */
	public List<Commit> getCommitList(int num)
	{
		List<Commit> list = new ArrayList<Commit>();
				

		
		int size = this.cmtCommitObjectList.size();
		for(int i = size-1; i >= 0; --i)
		{
			if(--num < 0)
				break;
			String k = this.cmtCommitObjectList.get(i);
			Commit cmt = this.cmtCommitObject.get(k);
			list.add(cmt);
		}
		
		
		return list;
	}
	
	/**
	 * 返回发生变化的业务类
	 * @param oldVersion
	 * @param newVersion
	 * @return
	 * @throws Exception 
	 * @throws DocumentException 
	 */
	public List<RelationClass> compareVersion(Version oldVersion, Version newVersion) throws DocumentException, Exception
	{
		List<RelationClass> differentClass = new ArrayList<RelationClass>();
		Iterator<Map.Entry<String, RelationClass>> iter = newVersion.getRelationClassIterator();
		while(iter.hasNext())
		{
			Map.Entry<String, RelationClass> entry = iter.next();
			String newSignature = entry.getValue().computeSignature();
			RelationClass newRc = entry.getValue();
			RelationClass oldRc = oldVersion.getRelationClass(entry.getKey());
			// 添加新的业务类
			if(oldRc == null)
			{
				differentClass.add(newRc);
				Upgrade.processNewBusiness(oldVersion, newVersion, newRc);
			}
			else
			{
				String oldSignature = oldRc.computeSignature();
				if(!newSignature.equals(oldSignature))
				{
					differentClass.add(newRc);
					
					Field[] fields = newRc.getClazz().getDeclaredFields();
					for(int i = 0; i < fields.length; ++i)
					{
						String fieldName = fields[i].getName();
						Field findOldField = null;
						try {
							findOldField = oldRc.getClazz().getDeclaredField(fieldName);
						} catch (NoSuchFieldException e) {
							// TODO: handle exception
							Upgrade.processNewField(oldVersion, newVersion, newRc, fields[i]);
						}

						// 判断是否发生变化
						if(findOldField != null)
						{
							String fieldTypeName = fields[i].getType().getName();
							String oldFieldTypeName = findOldField.getType().getName();
							if(!fieldTypeName.equals(oldFieldTypeName))
							{
								throw new UnsuportedFieldTypeChangeException();
							}
						}
						
						
					}
				}
			}
		}
		
		return differentClass;
	}
	
	/**
	 * 获取当前运行的业务版本
	 * @return
	 * @throws Exception 
	 */
	public Version getRunningTimeVersion() throws Exception
	{
		return instance.runningTimeVersion;
	}
}
